#pragma once
#include "base/scope_exit.hpp"
#include "event/cabinet.hpp"
#include "event/event.hpp"
#include "event/fd_event.hpp"
#include "event/signal_event.hpp"
#include "event/timer_event.hpp"

#include <cassert>
#include <chrono>
#include <cstdint>
#include <cstring>
#include <deque>
#include <fcntl.h>
#include <functional>
#include <iostream>
#include <memory>
#include <set>
#include <signal.h>
#include <unistd.h>
#include <unordered_map>
#include <vector>

#include <sys/epoll.h>

namespace {
inline uint64_t get_current_clock_milliseconds() {
    return std::chrono::duration_cast<std::chrono::milliseconds>(
               std::chrono::steady_clock::now().time_since_epoch())
        .count();
}
} // namespace

namespace ibox::event {
using Func = std::function<void()>;
using namespace std::chrono_literals;
using namespace ibox::cabinet;
class Loop;
class EpollLoop;
class EpollFdEvent;
class EpollFdSharedData;

class FuncItem {

public:
    explicit FuncItem(Func f)
        : func{std::move(f)},
          commit_time_point{std::chrono::steady_clock::now()} {}

public:
    Func func;
    std::chrono::steady_clock::time_point commit_time_point;
};

class Timer {
    using TimerCallback = std::function<void()>;

public:
    Timer(uint64_t interval_, uint64_t repeat_, TimerCallback cb_)
        : interval{interval_},
          expired{get_current_clock_milliseconds() + interval}, repeat{repeat_},
          cb{std::move(cb_)} {}

    //// 移动语义, 目前用不到
    // Timer(Timer &&other) noexcept
    //     : token{std::move(other.token)}, interval{other.interval},
    //       expired{other.expired}, repeat{other.expired} {}

    const uint64_t interval{};
    uint64_t expired{};
    uint64_t repeat{};
    TimerCallback cb{};

    // loop->add_timer时才设置token
    // std::unique_ptr<Token> token{};
    Token token{};
};

class TimerCompare {
public:
    bool operator()(const std::unique_ptr<Timer> &x,
                    const std::unique_ptr<Timer> &y) const {
        return x->expired > y->expired;
    }
};

// 水位线
class WaterLine {
public:
    size_t waiting_queue_size;
    std::chrono::milliseconds timer_delay{};       // 定时器延迟
    std::chrono::milliseconds run_next_delay{};    // run_next 执行延迟
    std::chrono::milliseconds run_callback_cost{}; // run回调时间消耗
};

// 峰值记录
class PeakRecord {
public:
    size_t waiting_queue_size{};
};

// 峰值限制
class PeakLimit {
public:
    size_t waiting_queue_size;
};

class EpollLoop {
public:
    EpollLoop() : m_epoll_fd{epoll_create1(EPOLL_CLOEXEC)} {
        assert(m_epoll_fd >= 0);
    }
    ~EpollLoop() {
        if (m_epoll_fd != -1) {
            close(m_epoll_fd);
            m_epoll_fd = -1;
        }
    }

public:
    void submit(Func func) {
        m_waiting_queue.emplace_back(func);

        auto queue_size = m_waiting_queue.size();
        if (queue_size > m_water_line.waiting_queue_size) {
            std::cout << "waiting queue size: " << queue_size << '\n';
        }
        if (queue_size > m_peak_record.waiting_queue_size) {
            m_peak_record.waiting_queue_size = queue_size;
        }
    }
    void run() {
        if (m_epoll_fd < 0) {
            return;
        }

        before_run();

        do_run();

        after_run();
    }
    void stop(std::chrono::milliseconds interval,
              Event::Mode mode = Event::Mode::Oneshot) {
        m_exit_timer_event = std::make_unique<TimerEvent>(this, interval, mode);
        m_exit_timer_event->set_callback([this] { stop_loop(); });
        m_exit_timer_event->enable();
    }

    void before_handle_one_event() {
        std::cout << "before_handle_one_event ...\n";
    }
    void after_handle_one_event() {
        std::cout << "after_handle_one_event ...\n";
    }

    int epoll_fd() const { return m_epoll_fd; }

    // timer添加到timer_min_heap中.
    // 并且通过timer_cabinet保存原始指针, 获取token
    Token *add_timer(std::unique_ptr<Timer> timer) {
        // 问题现象:
        // std::unique_ptr<Token> add_timer(Timer *timer) {
        // 很奇怪, 这里把timer指针放到堆里, 获取interval正常,
        // 但是后面使用的时候再获取interval就是乱值了? repeat 和 expired
        // 是正常的?
        // 解释:
        // 之前没有传递unique_ptr到下一层, 导致Timer对象析构了.
        std::cout << "add timer???\n";
        timer->token = m_timer_cabinet.save(timer.get());
        // timer_cabinet和timer_min_heap都存放了timer, 定义成unique_ptr不合适
        // 所以在cabinet中存放真实指针, 在min_heap中存放unique_ptr指针
        Token *res = &timer->token;
        m_timer_min_heap.emplace_back(std::move(timer));
        std::push_heap(
            m_timer_min_heap.begin(), m_timer_min_heap.end(), TimerCompare());
        // NOTE: timer已经被move了, 不能再直接使用.
        // 不过可以在move之前获取token指针的拷贝. return timer->token;
        return res;
    }

    void del_timer(Token *token) {
        std::cout << "del_timer ...\n";
        Timer *timer = m_timer_cabinet.free(token);
        if (timer) {
            timer->expired = 0;
            // 重新构建堆
            std::make_heap(m_timer_min_heap.begin(),
                           m_timer_min_heap.end(),
                           TimerCompare());
            std::pop_heap(m_timer_min_heap.begin(),
                          m_timer_min_heap.end(),
                          TimerCompare());
            m_timer_min_heap.pop_back();
        }
        // NOTE: 如果不使用unique_ptr的方式管理Timer, 可以考虑下面这种方式:
        // delete later
        // submit([timer] { delete timer; });
    }

    bool subscribe_signal(int signo, SignalSubscriber *who);

    bool unsubscribe_signal(int signo, SignalSubscriber *who);

    void on_signal() const;

    void add_fd_shared_data(int fd,
                            std::shared_ptr<EpollFdSharedData> &fd_data) {
        m_map_fd_data.insert(std::make_pair(fd, fd_data));
    }

    void remove_fd_shared_data(int fd) { m_map_fd_data.erase(fd); }

    std::shared_ptr<EpollFdSharedData> query_fd_shared_data(int fd) const {
        auto it = m_map_fd_data.find(fd);
        if (it != m_map_fd_data.end()) {
            return it->second;
        }
        return nullptr;
    }

private:
    void stop_loop() {
        std::cout << "stop looping ...\n";
        m_f_running = false;
    }
    void before_run() {
        std::cout << "before run ...\n";
        // int event_fd = CreateEventFd();
        // FdEvent read_event = CreateFdEvent("read_event");
        // if (read_event->initialize(event_fd, fdEvent::ReadEvent,
        // Event::Forever)
        //     != 0) {
        //     close(event_fd);
        //     delete read_event;
        //     return;
        // }
    }

    void do_run() {
        std::vector<epoll_event> events;
        events.resize(m_max_loop_entries);

        while (m_f_running) {
            int fds = epoll_wait(m_epoll_fd,
                                 events.data(),
                                 static_cast<int>(events.size()),
                                 static_cast<int>(get_wait_time()));

            before_run_one_round();

            handle_expired_timers();

            for (int i = 0; i < fds; i++) {
                epoll_event &ev = events.at(i);
                EpollFdEvent::on_event_callback(
                    ev.data.fd, ev.events, ev.data.ptr);
            }

            handle_next_func();

            // if (fds > m_max_loop_entries) {
            //     m_max_loop_entries += m_max_loop_entries / 2;
            //     events.resize(m_max_loop_entries);
            // }

            after_run_one_round();
        }
    }
    void after_run() { std::cout << "after run ...\n"; }

    int64_t get_wait_time() const {
        if (has_next_func()) {
            return 0;
        }

        // Get the top of minimum heap
        int64_t wait_time = -1;
        if (!m_timer_min_heap.empty()) {
            wait_time
                = static_cast<int64_t>(m_timer_min_heap.front()->expired
                                       - get_current_clock_milliseconds());
            // 过期时间小于当前时间, 即已经过期, 立即触发
            if (wait_time < 0) {
                wait_time = 0;
            }
        }
        return wait_time;
    }

    void before_run_one_round() { std::cout << "before_run_one_round ...\n"; }
    void after_run_one_round() { std::cout << "after_run_one_round ...\n"; }

    void handle_expired_timers() {
        std::cout << "handle_expired_timers ...\n";
        auto now = get_current_clock_milliseconds();
        while (!m_timer_min_heap.empty()) {
            Timer *timer = m_timer_min_heap.front().get();
            if (now < timer->expired) {
                break;
            }

            auto delay_ms = static_cast<int64_t>(now - timer->expired);
            if (delay_ms > (m_water_line.timer_delay.count())) {
                std::cout << "timer delay over waterline: " << delay_ms
                          << " ms\n";
            }

            // 在unique_ptr<Timer> 离开作用域之前执行回调函数
            if (timer->cb) {
                // FIXME: 什么时候timer会被删掉导致这里出现 Segmentation fault?
                // NOTE: 像下面这种方式会在离开作用域后删除timer指向的对象
                // std::unique_ptr<Timer> &timer = m_timer_min_heap.front();
                timer->cb();
            }

            // 将最小的值移动到 heap[last - 1]
            std::pop_heap(m_timer_min_heap.begin(),
                          m_timer_min_heap.end(),
                          TimerCompare());
            // 只剩下这一次
            if (timer->repeat == 1) {
                // 移除最后一个元素
                m_timer_min_heap.pop_back();
            } else {
                timer->expired += timer->interval;
                // 把最后一个元素放入heap中
                std::push_heap(m_timer_min_heap.begin(),
                               m_timer_min_heap.end(),
                               TimerCompare());
                timer->repeat--;
            }
        }
    }

    bool has_next_func() const { return !m_waiting_queue.empty(); }

    void handle_next_func() {
        while (!m_waiting_queue.empty()) {
            FuncItem item = m_waiting_queue.front();
            auto now = std::chrono::steady_clock::now();
            auto delay = now - item.commit_time_point;
            if (delay > m_water_line.run_next_delay) {
                auto delay_ms
                    = std::chrono::duration_cast<std::chrono::milliseconds>(
                        delay);
                std::cout << "run_next_delay: " << delay_ms.count() << "ms\n";
            }

            if (item.func) {
                item.func();
            }

            auto cost = std::chrono::steady_clock::now() - now;
            if (cost > m_water_line.run_callback_cost) {
                auto cost_ms
                    = std::chrono::duration_cast<std::chrono::milliseconds>(
                        cost);
                std::cout << "run_callback_cost: " << cost_ms.count() << "ms\n";
            }

            m_waiting_queue.pop_front();
        }
    }

    bool create_pipe(int &read_fd, int &write_fd) {
        int fds[2] = {0};
        if (pipe2(fds, O_CLOEXEC | O_NONBLOCK) != 0) {
            std::cerr << "pipe2() fail, errno=" << errno;
            return false;
        }

        read_fd = fds[0];
        write_fd = fds[1];
        return true;
    }

private:
    int m_max_loop_entries = 256;
    int m_epoll_fd = -1;
    bool m_f_running = true;
    std::unordered_map<int, std::shared_ptr<EpollFdSharedData>> m_map_fd_data;
    std::deque<FuncItem> m_waiting_queue;

    // Timer相关
    // 就只是用vector存放, 调用make_heap/push_heap/pop_heap构建/操作最小堆
    std::vector<std::unique_ptr<Timer>> m_timer_min_heap;
    Cabinet<Timer> m_timer_cabinet;
    std::unique_ptr<TimerEvent> m_exit_timer_event;

    // Signal相关
    std::unique_ptr<EpollFdEvent> m_signalfd_read_event;
    int m_signal_read_fd = -1;
    int m_signal_write_fd = -1;

    WaterLine m_water_line = {.waiting_queue_size = 16,
                              .timer_delay = 100ms,
                              .run_next_delay = 100ms};
    // PeakLimit m_peak_limit = {
    //     .waiting_queue_size = 32,
    // };
    PeakRecord m_peak_record = {
        .waiting_queue_size = 0,
    };
};

class Loop {
public:
    Loop() : m_impl{std::make_unique<EpollLoop>()} {}

public:
    void submit(Func func) { m_impl->submit(std::move(func)); }
    void run() { m_impl->run(); }
    void stop(std::chrono::milliseconds interval
              = std::chrono::milliseconds(1)) {
        m_impl->stop(interval);
    }

    EpollLoop *ptr() const { return m_impl.get(); }

private:
    std::unique_ptr<EpollLoop> m_impl;
};
} // namespace ibox::event
